// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef UI_EVENTS_GESTURE_DETECTION_TOUCH_DISPOSITION_GESTURE_FILTER_H_
#define UI_EVENTS_GESTURE_DETECTION_TOUCH_DISPOSITION_GESTURE_FILTER_H_

#include <stdint.h>

#include <queue>

#include "base/macros.h"
#include "ui/events/event_constants.h"
#include "ui/events/gesture_detection/bitset_32.h"
#include "ui/events/gesture_detection/gesture_detection_export.h"
#include "ui/events/gesture_detection/gesture_event_data_packet.h"

namespace ui {

// Interface with which the |TouchDispositionGestureFilter| forwards gestures
// for a given touch event.
class GESTURE_DETECTION_EXPORT TouchDispositionGestureFilterClient {
public:
    virtual void ForwardGestureEvent(const GestureEventData&) = 0;
};

// Given a stream of touch-derived gesture packets, produces a refined gesture
// sequence based on the ack dispositions of the generating touch events.
class GESTURE_DETECTION_EXPORT TouchDispositionGestureFilter {
public:
    explicit TouchDispositionGestureFilter(
        TouchDispositionGestureFilterClient* client);
    ~TouchDispositionGestureFilter();

    // To be called upon production of touch-derived gestures by the platform,
    // *prior* to the generating touch being forward to the renderer.  In
    // particular, |packet| contains [0, n] gestures that correspond to a given
    // touch event. It is imperative that a single packet is received for
    // *each* touch event, even those that did not produce a gesture.
    enum PacketResult {
        SUCCESS, // Packet successfully queued.
        INVALID_PACKET_ORDER, // Packets were received in the wrong order, i.e.,
        // TOUCH_BEGIN should always precede other packets.
        INVALID_PACKET_TYPE, // Packet had an invalid type.
    };
    PacketResult OnGesturePacket(const GestureEventDataPacket& packet);

    // OnTouchEventAck must be called upon receipt of every touch event ack.
    void OnTouchEventAck(uint32_t unique_event_id, bool event_consumed);

    // Whether there are any active gesture sequences still queued in the filter.
    bool IsEmpty() const;

private:
    // A single GestureSequence corresponds to all gestures created
    // between the first finger down and the last finger up, including gestures
    // generated by timeouts from a statinoary finger.
    typedef std::queue<GestureEventDataPacket> GestureSequence;

    // Utility class for maintaining the touch and gesture handling state for the
    // current gesture sequence.
    class GestureHandlingState {
    public:
        GestureHandlingState();

        // To be called on each touch event ack.
        void OnTouchEventAck(bool event_consumed, bool is_touch_start_event);

        // Returns true iff the gesture should be dropped.
        bool Filter(EventType type);

        // Whether an event of |type| has been filtered from the current sequence.
        bool HasFilteredGestureType(EventType type) const;

    private:
        // True iff the sequence has had any touch down event consumed.
        bool start_touch_consumed_;
        // True iff the most recently ack'ed touch event was consumed.
        bool current_touch_consumed_;
        // Indicates whether the previous gesture of a given type was dropped.
        BitSet32 last_gesture_of_type_dropped_;
        // Indicates whether *any* previous gesture of a given type was dropped.
        BitSet32 any_gesture_of_type_dropped_;
    };

    void FilterAndSendPacket(const GestureEventDataPacket& packet);
    void SendGesture(const GestureEventData& gesture,
        const GestureEventDataPacket& packet);
    void CancelTapIfNecessary(const GestureEventDataPacket& packet);
    void CancelFlingIfNecessary(const GestureEventDataPacket& packet);
    void EndScrollIfNecessary(const GestureEventDataPacket& packet);
    void PopGestureSequence();
    void SendAckedEvents();
    GestureSequence& Head();
    GestureSequence& Tail();

    TouchDispositionGestureFilterClient* client_;
    std::queue<GestureSequence> sequences_;

    GestureHandlingState state_;

    // Bookkeeping for inserting synthetic Gesture{Tap,Fling}Cancel events
    // when necessary, e.g., GestureTapCancel when scrolling begins, or
    // GestureFlingCancel when a user taps following a GestureFlingStart.
    int ending_event_motion_event_id_;
    MotionEvent::ToolType ending_event_primary_tool_type_;
    bool needs_tap_ending_event_;
    bool needs_show_press_event_;
    bool needs_fling_ending_event_;
    bool needs_scroll_ending_event_;

    DISALLOW_COPY_AND_ASSIGN(TouchDispositionGestureFilter);
};

} // namespace ui

#endif // UI_EVENTS_GESTURE_DETECTION_TOUCH_DISPOSITION_GESTURE_FILTER_H_
